(* driver.ml
 *
 * This file is part of camEngine
 * 
 * camEngine - A 3d motor for ocaml
 * Copyright (C) 2009 DAVY Guillaume <davyg2@gmail.com> - ISMAIL Daline - COCHU Alexandre
 *
 * camEngine is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * camEngine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with camEngine.  If not, see <http://www.gnu.org/licenses/>.
 *)

open Vec2d;;
open Vec3d;;
open Graphics;;
open Scanf;;
open Scanning;;
open Array;;
open Mesh;;
open Array;;
open Matrice;;

let black=black;;
let white=white;;
let red=red;;
let green=green;;
let blue=blue;;
let yellow=yellow;;
let cyan=cyan;;
let magenta=magenta;;

let sof = string_of_float;;
let iof = int_of_float;;
let foi = float_of_int;;

let color = ref 0;;

let swap (a:vec3d) (b:vec3d) =
	let c = Oo.copy b in
	a#construct b;
	b#construct c;
;;

class driver = object (self)
	
	val mutable zoom = 100.
	val mutable zbuffer = make_matrix 1 1 0.
	val mutable far = 999.
	val mutable world = new matrice4
	val mutable view = new matrice4
	val mutable projection = new matrice4
	
	method init (dim:vec2d) (pos:vec2d) (txt:string) =
		Graphics.open_graph (" " ^ sof(dim#get_x) ^ "x" ^ sof(dim#get_y) ^ "+" ^ sof(pos#get_x) ^ "-" ^ sof(pos#get_y));
		Graphics.auto_synchronize false;
		
	method close =
		Graphics.close_graph ();
	
	
	method preRender =
		self#clear;
		let dim = self#get_size in
		zbuffer <- (make_matrix (iof(dim#get_x)) (iof(dim#get_y)) far);
		
	method postRender =
		Graphics.synchronize ();
	
	method plot (p:vec3d) (c:int) =
		if (p#get_z < zbuffer.(iof p#get_x).(iof p#get_y)) then
		(
print_float p#get_x;
print_string " ";
print_float p#get_y;
print_string "\n";
			Graphics.set_color c;
			Graphics.plot (iof p#get_x) (iof p#get_y);
			zbuffer.(iof p#get_x).(iof p#get_y) <- p#get_z
		)
	
	method line (pe1:vec3d) (pe2:vec3d) (c:int) =
print_string "hello\n";
		let p1 = Oo.copy pe1 and p2 = Oo.copy pe2 in
		let dp = Oo.copy pe1 in
		dp#reverse;
		dp#move p2;
		let a = ref (dp#get_y /. dp#get_x) in
		(*self#plot p1 c;
		self#plot p2 c;*)
(*print_float !a;
print_string " ";
print_string "\n";*)
		
		if (abs_float(!a)>1.) then
		begin
			a:=(1./. !a);
			p1#swapxy;
			p2#swapxy;
			if (p1#get_x > p2#get_x) then
				(swap p1 p2);
			let rec aux (p:vec3d) =
			p#set_y (p1#get_y +. (!a *. (p#get_x -. p1#get_x)));
			p#swapxy;
print_float p1#get_x;
print_string " ";
print_float p1#get_y;
print_string " ";
print_float p2#get_x;
print_string " ";
print_float p2#get_y;
print_string " ";
print_float p#get_x;
print_string " ";
print_float p#get_y;
print_string "\n";
			self#plot p c;
			p#swapxy;
			if p#get_x < p2#get_x then
			begin
				p#move (new vec3d 1. 0. 0.);
				aux p;
			end
			in
			aux (Oo.copy p1);
		end
		else
		begin
			if (p1#get_x > p2#get_x) then
				(swap p1 p2);
			let rec aux (p:vec3d) =
			p#set_y (p1#get_y +. (!a *. (p#get_x -. p1#get_x)));
print_float p1#get_x;
print_string " ";
print_float p1#get_y;
print_string " ";
print_float p2#get_x;
print_string " ";
print_float p2#get_y;
print_string " ";
print_float p#get_x;
print_string " ";
print_float p#get_y;
print_string "\n";
			self#plot p c;
			if p#get_x < p2#get_x then
			begin
				p#move (new vec3d 1. 0. 0.);
				aux p;
			end
			in
			aux (Oo.copy p1);
		end;
	
	(*method face ((tp1:vec3d),(tp2:vec3d),(tp3:vec3d)) (c:int) =
		Graphics.set_color c;
		let p1 = Oo.copy tp1 in
		let p2 = Oo.copy tp2 in
		let p3 = Oo.copy tp3 in
		self#fit p1;
		self#fit p2;
		self#fit p3;
		let drawline ((tp1:vec2d),(tp2:vec2d)) = ()
			(*let depX = new vec2d 1. 0.
			let depY = new vec2d 0. 1.
			let dp = Oo.copy tp2 in
			 dp#scale -1;
			 dp#move tp1;
			 if (dp#get_x <> 0) then
			  if (dp#get_x > 0) then
			   if (dp#get_y <> 0) then
			    if (dp#get_y > 0) then
			     if (dp#get_x >= dp#get_y) then
			      let e = dp#get_x in
			       dp#scale 2;
			       self#plot tp1 c d;
			       tp1#move depX;
			       while (tp1#get_x <> tp2#get_x)
			       *)
		in
		(*TODO:make our own draw poly in order to call our own plot to include zbuffer*)
		Graphics.fill_poly [| (iof(p1#get_x), iof(p1#get_y)) ; (iof(p2#get_x), iof(p2#get_y)) ; (iof(p3#get_x), iof(p3#get_y)) |];*)
	
	
	method project (p:vec3d) =
		new vec2d (p#get_x /. p#get_z) (p#get_y /. p#get_z);
			
	method projTri ((p1:vec3d),(p2:vec3d),(p3:vec3d)) =
		(self#project p1,self#project p2,self#project p3);
	
	
	method drawFace (t:(vec3d*vec3d*vec3d)) (c:int) =
		print_string "drawface launch\n";
		let mat = new matrice4 in
		mat#mul projection;
		mat#mul view;
		mat#mul world;
		let (a,b,c) = t in
		let x1 = a#get_x and y1 = a#get_y and z1 = a#get_z in
		let x2 = b#get_x and y2 = b#get_y and z2 = b#get_z in
		let x3 = c#get_x and y3 = c#get_y and z3 = c#get_z in
		Graphics.fill_poly [| (iof(x1/.z1), iof(y1/.z1)) ; (iof(x2/.z2), iof(y2/.z2)) ; (iof(x3/.z3), iof(y3/.z3)) |];
		
	method drawFaces p i =
		match i
		with []	-> ();
		| h::q	-> let (a,b,c,d) = h in self#drawFace ( p.(a) , p.(b) , p.(c) ) d; (self#drawFaces p q);
	
	
	method openFile path =
		from_file path;
	
	method getMesh path =
		let (n,v,i) = self#readObj (self#openFile path) in
		new mesh n v i;
	
	
	method set_far f =
		far <- f;
	
	method set_world w =
		world <- Oo.copy w;
	
	method set_view t =
		view <- Oo.copy t;
	
	method set_projection t =
		view <- Oo.copy t;
	
	method get_far =
		far;
	
	method get_world =
		Oo.copy world;
	
	method get_view =
		Oo.copy view;
	
	method get_projection =
		Oo.copy projection;
	
	method set_title title =
		Graphics.set_window_title title;
	
	
	method private fit (p:vec2d) =
		let s = self#get_size in
		s#resize 0.5;
		p#resize zoom;
		p#move s;
		
	method private clear =
		Graphics.clear_graph ();
	
	method private readObj buf =
		let rf = self#readObj in
		match buf
		with	b when (end_of_input b)	-> ("" , [| |] , [])
		|		b						->
			let (c , str) = bscanf b "%s%s@\n" (fun x y -> (x , y)) in
			(
			match c
				with "#"	-> (rf b)
				| "g"		-> let (n,p,f) = (rf b) in (str,p,f)
				| "v"		->
					let l = (bscanf (from_string str) "%s %f %f %f" (fun a x y z -> (new vec3d x y z) ) ) in
					let (n,p,f) = (rf b) in
					(n,(append ([|l|]) p) , f)
				| "f"		-> let l = (bscanf (from_string str) "%s %i %i %i" (fun a x y z -> (x-1,y-1,z-1) ) ) in let (n,p,f) = (rf b) in (n,p,l::f)
				|_			-> (rf b)
			)
	
	method private get_size =
		new vec2d (foi (Graphics.size_x ())) (foi (Graphics.size_y ()));
		
end;;

